package com.easyctba.core.service.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.easyctba.common.core.domain.R;
import com.easyctba.common.core.utils.MapstructUtils;
import com.easyctba.common.core.utils.StringUtils;
import com.easyctba.common.mybatis.core.page.TableDataInfo;
import com.easyctba.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.easyctba.common.satoken.utils.LoginHelper;
import com.easyctba.core.domain.bo.FrontProjectBo;
import com.easyctba.core.domain.bo.VideoUploadBo;
import com.easyctba.core.enums.ProjectType;
import com.easyctba.core.service.ICtbaEntropyService;
import com.easyctba.core.service.ICtbaFiasService;
import com.easyctba.core.service.ICtbaStService;
import com.easyctba.resource.api.RemoteFileService;
import com.easyctba.resource.api.domain.RemoteFile;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.easyctba.core.domain.bo.CtbaProjectBo;
import com.easyctba.core.domain.vo.CtbaProjectVo;
import com.easyctba.core.domain.CtbaProject;
import com.easyctba.core.mapper.CtbaProjectMapper;
import com.easyctba.core.service.ICtbaProjectService;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

import static com.easyctba.core.enums.OssConst.*;

/**
 * 工程Service业务层处理
 *
 * @author Huang
 * @date 2024-02-09
 */
@RequiredArgsConstructor
@Service
public class CtbaProjectServiceImpl implements ICtbaProjectService {

    private final CtbaProjectMapper baseMapper;

    private final ICtbaEntropyService entropyService;

    private final ICtbaStService stService;

    private final ICtbaFiasService fiasService;

    @DubboReference
    private RemoteFileService remoteFileService;

    /**
     * 查询工程
     */
    @Override
    public CtbaProjectVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询工程列表
     */
    @Override
    public TableDataInfo<CtbaProjectVo> queryPageList(CtbaProjectBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<CtbaProject> lqw = buildQueryWrapper(bo);
        Page<CtbaProjectVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询工程列表
     */
    @Override
    public List<CtbaProjectVo> queryList(CtbaProjectBo bo) {
        LambdaQueryWrapper<CtbaProject> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<CtbaProject> buildQueryWrapper(CtbaProjectBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<CtbaProject> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, CtbaProject::getUserId, bo.getUserId());
        lqw.like(StringUtils.isNotBlank(bo.getProjectName()), CtbaProject::getProjectName, bo.getProjectName());
        lqw.eq(StringUtils.isNotBlank(bo.getProjectType()), CtbaProject::getProjectType, bo.getProjectType());
        return lqw;
    }

    /**
     * 新增工程
     */
    @Override
    public Boolean insertByBo(CtbaProjectBo bo) {
        CtbaProject add = MapstructUtils.convert(bo, CtbaProject.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改工程
     */
    @Override
    public Boolean updateByBo(CtbaProjectBo bo) {
        CtbaProject update = MapstructUtils.convert(bo, CtbaProject.class);
        if (update != null) {
            boolean b = validEntityBeforeSave(update);
            if (b) return baseMapper.updateById(update) > 0;
        }
        return false;
    }

    /**
     * 保存前的数据校验
     * 1.检验项目名是否重名
     */
    private boolean validEntityBeforeSave(CtbaProject entity){
        Long userId = LoginHelper.getUserId();
        entity.setUserId(userId);
        //校验是否存在
        LambdaQueryWrapper<CtbaProject> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(userId!=null,CtbaProject::getUserId,userId)
            .eq(StringUtils.isNotBlank(entity.getProjectName()),CtbaProject::getProjectName,entity.getProjectName());
        List<CtbaProject> ctbaProjects = baseMapper.selectList(wrapper);
        return ctbaProjects.isEmpty();
    }

    /**
     * 批量删除工程
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public TableDataInfo<CtbaProjectVo> queryPageListByUser(FrontProjectBo projectBo, PageQuery pageQuery) {
        LambdaQueryWrapper<CtbaProject> lqw = Wrappers.lambdaQuery();
        Long userId = LoginHelper.getUserId();
        lqw.eq(userId!=null,CtbaProject::getUserId,userId);
        lqw.like(StringUtils.isNotBlank(projectBo.getProjectName()),CtbaProject::getProjectName,projectBo.getProjectName());
        lqw.eq(StringUtils.isNotBlank(projectBo.getProjectType()),CtbaProject::getProjectType,projectBo.getProjectType());

        Page<CtbaProjectVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    @GlobalTransactional
    public R<Long> insertByUser(FrontProjectBo bo) {
        CtbaProject convert = MapstructUtils.convert(bo, CtbaProject.class);
        if (convert==null){
            return R.fail("新增项目参数为空");
        }
        //校验
        if (!validEntityBeforeSave(convert)) {
            return R.fail("数据校验失败，项目名不能重名");
        }
        boolean b= baseMapper.insert(convert)>0;
        //插入项目表
        if (!b) {
            return R.fail("项目新建异常");
        }
        //2.初始化对应项目类型的表
        String projectType = convert.getProjectType();
        switch (projectType) {
            case ProjectType.InformationEntropy -> {
                Boolean b1 = entropyService.initEntropy(convert.getId());
                if (!b1) {
                    return R.fail("信息熵计算初始化失败");
                }
            }
            case ProjectType.ST -> {
                Boolean b1 = stService.initST(convert.getId());
                if (!b1) {
                    return R.fail("ST分析初始化失败！");
                }
            }
            case ProjectType.FIAS -> {
                Boolean b1 = fiasService.initFIAS(convert.getId());
                if (!b1) {
                    return R.fail("FIAS分析初始化失败！");
                }
            }
            default -> {
                //TODO 其他分析
            }
        }
        return R.ok(convert.getId());
    }

    @Override
    public CtbaProjectVo queryByIdAndUser(Long id) {
        Long userId = LoginHelper.getUserId();
        LambdaQueryWrapper<CtbaProject> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(id!=null,CtbaProject::getId,id)
            .eq(userId!=null,CtbaProject::getUserId,userId);
        return baseMapper.selectVoOne(wrapper);
    }

    @Override
    public Map<String, Object> getOssSign() {
        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
//        Date date = new Date();
//        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
//        String now = format.format(date);
        Map<String, Object> respMap = null;

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessId, accessKey);
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            // PostObject请求最大可支持的文件大小为5 GB，即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, "video");

            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);
            respMap = new LinkedHashMap<>();
            //这些参数名必须要这样写，与官方文档一一对应
            respMap.put("OSSAccessKeyId", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("Signature", postSignature);
            respMap.put("dir", "video");
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            //让服务端返回200,不然，默认会返回204
            respMap.put("success_action_status", 200);
        }
        catch (Exception e) {
            // Assert.fail(e.getMessage());
            System.out.println(e.getMessage());
        }
        finally {
            ossClient.shutdown();
        }
        return respMap;
    }

    @Override
    public R<Void> uploadVideo(VideoUploadBo bo) {
        String type = bo.getType();
        Boolean b = null;
            switch (type){
            //ST分析类型
            case ProjectType.ST-> {
               b= stService.sTUploadVideo(bo.getId(), bo.getUrl());
            }
            case ProjectType.FIAS -> {
                b=fiasService.fiasUploadVideo(bo.getId(),bo.getUrl());
            }
            case ProjectType.SP -> {
                //TODO
            }
        }
        return Boolean.TRUE.equals(b) ?R.ok():R.fail("视频状态更新失败");
    }

    /**
     * 生成视频封面
     * @return 生成路径
     */
    @Override
    public R<String> setVideoCover(MultipartFile file, String type, Long id) {
        try {
            RemoteFile upload = remoteFileService.upload(file.getName(), file.getOriginalFilename(), file.getContentType(), file.getBytes());
            Long ossId = upload.getOssId();
            Boolean b = null;
            switch (type) {
                //ST分析类型
                case ProjectType.ST -> {
                    b = stService.updateCover(ossId, id);
                }
                case ProjectType.FIAS -> {
                    //TODO
                }
                case ProjectType.SP -> {
                    //TODO
                }
            }
            return Boolean.TRUE.equals(b) ?R.ok(upload.getUrl()):R.fail("视频封面更新失败");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
